package se.energimyndigheten.lampguiden.bulbreplacement;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.preference.PreferenceManager;

import org.json.JSONException;

import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;

import se.energimyndigheten.lampguiden.LightSource;
import se.energimyndigheten.lampguiden.util.AssetsUtil;

public class ComparisonCalculator {

    //private static final String TAG = ComparisonCalculator.class.getSimpleName();

    private int mCalculationHours;
    private double mCalculationElectricityPrice;
    private int mBasePower;
    private LightSource[] mLightSources;
    private LightSource mBaseLightSource;
    private Map<Integer, String> mConversionTable;
    private double usedEnergyNormalized;
    private double usedEnergyCost;
    private double lampsCost;
    private static SharedPreferences mPreferences;
    private int mLumen;

    public static ComparisonCalculator initiateCalculator(Context ctx, LightSource baseLight, int basePower, Map<Integer, String> conversionTable)
            throws JSONException, IOException {
        //Log.v(TAG, "initiateCalculator");
        AssetManager am = ctx.getAssets();
        ComparisonCalculator cc = AssetsUtil.initComparsionCalculatorFromJson(AssetsUtil.getFileContents(am, AssetsUtil.JSON_PATH_CALCULATIONS));
        cc.setConversionTable(conversionTable);
        cc.setBaseLight(baseLight);
        cc.setBasePower(basePower);
        cc.setContext(ctx);
        cc.calculateBaseLight();
        return cc;
    }

    private void setContext(Context ctx) {
        mPreferences = PreferenceManager.getDefaultSharedPreferences(ctx);
    }

    public ComparisonCalculator(int totalHours, double electricityPrice, LightSource[] comparableLights) {
        mCalculationHours = totalHours;
        mCalculationElectricityPrice = electricityPrice;
        mLightSources = comparableLights;
    }

    private LightSource getLightSourceByType(String type) {
        for (LightSource src : mLightSources) {
            if (type.equals(src.getType()))
                return src;
        }
        return null;
    }

    /**
     * Calculate the cost of the selected light source.
     * This will be used for the comparison later.
     */
    public void calculateBaseLight() {
        //Log.v(TAG, "calculateBaseLight");

        LightSource base = getLightSourceByType(mBaseLightSource.getLampType());
        //Log.v(TAG, " - base: " + base.getName());

        String range = mConversionTable.get(mBasePower);
        mLumen = getMaxOfRange(range);
        double baseLife = base.getLifeExpetancy();
        double basePrice = base.getDefaultPrice(mLumen);
        double basePowerNeeded = base.getPower();

        //Log.v(TAG, " - mCalculationHours:" + mCalculationHours);
        //Log.v(TAG, " - baseLife:" + baseLife);

        // cost of buying light sources
        double lampsNeeded = mCalculationHours / baseLife;
        lampsCost = lampsNeeded * basePrice;

        //Log.v(TAG, " - lampCost: " + lampsCost);

        // energy cost
        double usedEnergy = mBasePower / 1000.0 * mCalculationHours;
        usedEnergyCost = usedEnergy * mCalculationElectricityPrice;
        //Log.v(TAG, " - used energy cost: " + usedEnergyCost);
        usedEnergyNormalized = usedEnergy / basePowerNeeded;
        //Log.v(TAG, " - normalized: " + usedEnergyNormalized);
    }

    /**
     * Get all light sources that are comparable with the selected one. Lumen and type must match.
     * @return An array of comparable light sources.
     */
    public LightSource[] getComparableLightSources() {
        //Log.v(TAG, "getComparableLightSources * * * * *");

        LightSource base = getLightSourceByType(mBaseLightSource.getLampType());

        // Find comparable lights
        ArrayList<LightSource> res = new ArrayList<>();
        for (LightSource candidate : mLightSources) {
            // match type and lumen
            if (mLumen < candidate.getMaxLumen() && base.hasReplacement(candidate.getType())) {
                int price = candidate.getDefaultPrice(mLumen);
                candidate.setDefaultPrice(price);
                candidate.setPrice(loadPrice(candidate, price));
                candidate.setMySavings(getCostComparison(candidate, price));
                res.add(candidate);
            }
        }
        // Sort collection and return as array
        Collections.sort(res, new LightComparator());
        LightSource[] result = new LightSource[res.size()];
        for (int i = 0; i < res.size(); i++)
            result[i] = res.get(i);
        return result;
    }

    // For list sorting
    public class LightComparator implements Comparator<LightSource> {
        @Override
        public int compare(LightSource o1, LightSource o2) {
            return o1.getEnergyRank() - o2.getEnergyRank();
        }
    }

    public int getCostComparison(LightSource light) {
        return getCostComparison(light, light.getPrice());
    }

    /**
     * Calculate savings from using a certain light source compared to other
     * @return savings using the light source at index
     */
    public int getCostComparison(LightSource light, int price) {
        //Log.v(TAG, "getCostComparison - " + light.getName());
        if (price == -1) {
            throw new InvalidParameterException("Invalid price: " + price);
        }
        int lifeExpectancy = light.getLifeExpetancy();
        double powerNeed = light.getEffiency();
        //Log.v(TAG, "  - expected life: " + lifeExpectancy);
        double lampsNeeded = mCalculationHours / (double) lifeExpectancy;
        //Log.v(TAG, " - lamps: " + lampsNeeded + " + price: " + price);
        double lampsPurchaseCost = lampsNeeded * price;
        //Log.v(TAG, " - cost:" + lampsPurchaseCost);

        // mLumen/watt depends on light source type (efficiency)
        double usedEnergy = usedEnergyNormalized * powerNeed;
        double thisUsedEnergyCost = usedEnergy * mCalculationElectricityPrice;
        //Log.v(TAG, " - energyCost: " + thisUsedEnergyCost);
        double totalSaving = lampsCost + usedEnergyCost - lampsPurchaseCost - thisUsedEnergyCost;
        //double perLampSaving = totalSaving / lampsNeeded;
        //Log.v(TAG, " - per lamp saving: " + (int) Math.round(perLampSaving / 5) * 5);
        //Log.v(TAG, " - total savings: " + (int) Math.round(totalSaving / 5) * 5);
        return (int) Math.round(totalSaving / 5) * 5;
    }

    private int getMaxOfRange(String range) {
        // Range is currently stored as a string with comma separated integers
        int comma = range.lastIndexOf(",");
        if (comma == -1)
            return -1;
        String val = range.substring(comma + 1);
        return Integer.parseInt(val);
    }

    public void setBaseLight(LightSource baseLight) {
        mBaseLightSource = baseLight;
    }

    private void setBasePower(int basePower) {
        mBasePower = basePower;
    }

    private void setConversionTable(Map<Integer, String> conversionTable) {
        mConversionTable = conversionTable;
    }

    private String getPriceTag(LightSource light) {
        return "MyPrice." + mBasePower + "." + light.getName();
    }

    public int loadPrice(LightSource light, int fallback) {
        return mPreferences.getInt(getPriceTag(light), fallback);
    }

    public void savePrice(int price, LightSource light) {
        SharedPreferences.Editor editor = mPreferences.edit();
        editor.putInt(getPriceTag(light), price);
        editor.commit();
    }
}
